﻿using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Miris.HumanMilkAnalyser.DataModel;
using Miris.HumanMilkAnalyser.Service;
using Miris.HumanMilkAnalyser.Service.Shared;
using Miris.HumanMilkAnalyser.WPF.ViewModel;
using Miris.HumanMilkAnalyser.WPF.ViewModel.TestItemViewModel;

namespace Miris.HumanMilkAnalyser.WPF.Shared
{
    /// <summary>
    /// Provide some shared methods used within ViewModel or for ViewModel
    /// </summary>
    public static class ViewModelHelper
    {
        /// <summary>
        /// Fill the provided ObservableCollection of AllTestItemsViewModel based on provided list of AnalysisResult.
        /// </summary>
        /// <param name="analysisResults">A list of AnalysisResult.</param>
        /// <param name="allTestItemsViewModelCollection">The provided ObservableCollection of AllTestItemsViewModel will be filled.</param>
        public static void FillAllTestItemsViewModelCollection(IList<AnalysisResult> analysisResults,
            ObservableCollection<AllTestItemsViewModel> allTestItemsViewModelCollection)
        {
            allTestItemsViewModelCollection.Clear();

            for (int i = 0; i < analysisResults.Count; i++)
            {
                var allAnalysisResultsViewModel = FillAllTestItemsViewModel(analysisResults[i]);
                allAnalysisResultsViewModel.SequenceNumber = i + 1;

                allTestItemsViewModelCollection.Add(allAnalysisResultsViewModel);
            }
        }

        /// <summary>
        /// Fill the provided ObservableCollection of AllTestItemsViewModel based on provided list of AnalysisResult.
        /// </summary>
        /// <param name="analysisResult">A instance of AnalysisResult.</param>
        /// <returns>A instance of AllTestItemsViewMod with filled data.</returns>
        public static AllTestItemsViewModel FillAllTestItemsViewModel(AnalysisResult analysisResult)
        {
            var allAnalysisResultsViewModel = new AllTestItemsViewModel(null, (MilkStage)analysisResult.SampleStageType)
            {
                CurrentAnalysisResult = analysisResult,
                SampleNumber = analysisResult.SampleNumber,
                TestDateDisplay = analysisResult.TestDate.ToString(GeneralUtility.MediumDateTimeFormat),
                SampleType = GetSampleTypeDescription(analysisResult.SampleType),
                SampleStage = GetSampleStageDescription(analysisResult.SampleStageType),
                DiagnosticMessageList = from p in analysisResult.AnalysisResultDiagnosticMessages
                                        select new DiagnosticMessageReference { MessageContent = p.MessageContent }
            };

            allAnalysisResultsViewModel.Fat.TestResult = analysisResult.Fat;
            allAnalysisResultsViewModel.CrudeProtein.TestResult = analysisResult.CrudeProtein;
            allAnalysisResultsViewModel.TrueProtein.TestResult = analysisResult.TrueProtein;
            allAnalysisResultsViewModel.Carbohydrate.TestResult = analysisResult.Carbohydrate;
            allAnalysisResultsViewModel.TS.TestResult = analysisResult.TS;
            allAnalysisResultsViewModel.Energy.TestResult = analysisResult.Energy;

            return allAnalysisResultsViewModel;
        }

        /// <summary>
        /// Get the sample type description based on provided sample type.
        /// </summary>
        /// <param name="sampleType">The provided sample type.</param>
        /// <returns>The description includes 前段、中段、后段和全乳.</returns>
        public static string GetSampleTypeDescription(byte sampleType)
        {
            var sample = (MilkSample)sampleType;
            var sampleDescription = string.Empty;

            switch (sample)
            {
                case MilkSample.Front:
                    sampleDescription = "前段";
                    break;
                case MilkSample.Middle:
                    sampleDescription = "中段";
                    break;
                case MilkSample.End:
                    sampleDescription = "后段";
                    break;
                case MilkSample.Full:
                    sampleDescription = "全乳";
                    break;
            }

            return sampleDescription;
        }

        /// <summary>
        /// Get the sample stage description based on provided sample stage.
        /// </summary>
        /// <param name="sampleStage">The provided sample stage.</param>
        /// <returns>The description includes 初乳、过渡乳和成熟乳.</returns>
        public static string GetSampleStageDescription(byte sampleStage)
        {
            var stage = (MilkStage)sampleStage;
            var stageDescription = string.Empty;

            switch (stage)
            {
                case MilkStage.Unmature:
                    stageDescription = "初乳";
                    break;
                case MilkStage.Transitional:
                    stageDescription = "过渡乳";
                    break;
                case MilkStage.Mature:
                    stageDescription = "成熟乳";
                    break;
            }

            return stageDescription;
        }

        /// <summary>
        /// Check setting if current is working offline.
        /// </summary>
        /// <returns>True means current is working offline. False means current is working online.</returns>
        public static bool CheckIfWorkingOffline()
        {
            return bool.Parse((string)ConfigFileUtility.GetValue(ConfigurationConstant.IsWorkingOffline));
        }

        /// <summary>
        /// Get the local mini log file full name. If file does not exist then exception will occur.
        /// </summary>
        /// <returns>The local mini log file full name.</returns>
        public static string GetLocalMiniLogFileFullName()
        {
            // Get the minilog file directory full name from config file.
            var localMiniLogFileDirectoryFullName = Path.Combine(Environment.CurrentDirectory, (string)ConfigFileUtility.GetValue(ConfigurationConstant.LocalMiniLogFileDirectoryName));
            var localMiniLogFileFullName = Path.Combine(localMiniLogFileDirectoryFullName, (string)ConfigFileUtility.GetValue(ConfigurationConstant.MiniLogFileName));

            // Check if the minilog exists.
            if (!File.Exists(localMiniLogFileFullName))
            {
                throw new ArgumentException("未找到本地同步的设备数据文件，请联系设备提供商");
            }

            return localMiniLogFileFullName;
        }

        /// <summary>
        /// Retrieve analysis result from minilog file from device based on provided sample number and milk stage.
        /// </summary>
        /// <param name="sampleNumber">The provided sample number.</param>
        /// <param name="selectedMilkStage">The provided milk stage.</param>
        /// <returns>List of result data.</returns>
        public static IList<AllTestItemsViewModel> RetrieveAnalysisResultFromMiniLog(string sampleNumber, MilkStage selectedMilkStage)
        {
            // If NOT working offline, then system will sync minilog from device.
            if (!CheckIfWorkingOffline())
            {
                MiniLogHelper.SyncMiniLogFileFromDevice();
            }

            var genuineCheckService = new GenuineCheckService();
            var serialNumber = genuineCheckService.GetSerialNumber();
            var genuineCheckResult = genuineCheckService.Check(serialNumber);

            if (genuineCheckResult == GenuineCheckStatus.Failed)
            {
                throw new Exception("无效系统注册码，请与系统提供商联系以获取技术支持。");
            }

            var localMiniLogFileFullName = GetLocalMiniLogFileFullName();
            var allTestItemsViewModelList = new List<AllTestItemsViewModel>();

            // Open minilog file
            using (var fs = new FileStream(localMiniLogFileFullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                using (var sr = new StreamReader(fs, Encoding.Default))
                {
                    string currentLine = string.Empty;
                    string[] arrayData = null;
                    int sequenceNumber = 1;

                    while (sr.Peek() != -1)
                    {
                        // Read current line content
                        currentLine = sr.ReadLine();

                        // Check if current line starts with input sample number
                        if (currentLine.StartsWith("M", StringComparison.OrdinalIgnoreCase) && 
                            currentLine.IndexOf(sampleNumber, 1, StringComparison.OrdinalIgnoreCase) != -1)
                        {
                            // Convert the data into string array.
                            arrayData = currentLine.Split(',');

                            // Check the sample number format.
                            // Part 1, M.
                            // Part 2, {Sample Number}.
                            // Part 3, 4 Numbers.
                            // Example, MYBH201307180010048.
                            if (arrayData[0].Substring(1, arrayData[0].Length - 5).Equals(sampleNumber, StringComparison.OrdinalIgnoreCase))
                            {
                                // Get the test date.
                                DateTime testDate = Convert.ToDateTime(string.Format("{0} {1}", arrayData[1], arrayData[2]));

                                // Create instance of AllTestItemsViewModel.
                                allTestItemsViewModelList.Add(new AllTestItemsViewModel(null, selectedMilkStage)
                                {
                                    SequenceNumber = sequenceNumber,
                                    TestDate = testDate,
                                    TestDateDisplay = testDate.ToString(GeneralUtility.MediumDateTimeFormat),
                                    Fat = new TestItemFatViewModel(null, SharedEntityStorage.AnalysisItemList, selectedMilkStage)
                                    {
                                        TestResult = double.Parse(arrayData[34])
                                    },
                                    CrudeProtein = new TestItemCrudeProteinViewModel(null, SharedEntityStorage.AnalysisItemList, selectedMilkStage)
                                    {
                                        TestResult = double.Parse(arrayData[35])
                                    },
                                    Carbohydrate = new TestItemCarbohydrateViewModel(null, SharedEntityStorage.AnalysisItemList, selectedMilkStage)
                                    {
                                        TestResult = double.Parse(arrayData[36])
                                    },
                                    TS = new TestItemTSViewModel(null, SharedEntityStorage.AnalysisItemList, selectedMilkStage)
                                    {
                                        TestResult = double.Parse(arrayData[37])
                                    },
                                    Energy = new TestItemEnergyViewModel(null, SharedEntityStorage.AnalysisItemList, selectedMilkStage)
                                    {
                                        TestResult = double.Parse(arrayData[38])
                                    },
                                    TrueProtein = new TestItemTrueProteinViewModel(null, SharedEntityStorage.AnalysisItemList, selectedMilkStage)
                                    {
                                        TestResult = string.IsNullOrEmpty(arrayData[39]) ? 999 : double.Parse(arrayData[39])
                                    }
                                });

                                sequenceNumber++;
                            }
                        }
                    }
                }
            }

            return allTestItemsViewModelList;
        }

        /// <summary>
        /// Retrieve the all sample numbers list from device based on the provided date range.
        /// </summary>
        /// <param name="startDate">The start date.</param>
        /// <param name="endDate">The end date.</param>
        /// <returns>A list of unassigned sample number.</returns>
        public static IList<UnassignedSampleNumberViewModel> RetrieveDeviceAllSampleNumberList(DateTime startDate, DateTime endDate)
        {
            // If NOT working offline, then system will sync minilog from device.
            if (!CheckIfWorkingOffline())
            {
                MiniLogHelper.SyncMiniLogFileFromDevice();
            }

            var localMiniLogFileFullName = GetLocalMiniLogFileFullName();
            var deviceAllSampleNumberList = new List<UnassignedSampleNumberViewModel>();

            // Open minilog file
            using (var fs = new FileStream(localMiniLogFileFullName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
            {
                using (var sr = new StreamReader(fs, Encoding.Default))
                {
                    string currentLine = string.Empty;
                    string sampleNumber = string.Empty;
                    string[] arrayData = null;

                    while (sr.Peek() != -1)
                    {
                        // Read current line content
                        currentLine = sr.ReadLine();

                        // Check if current line contains analysis result.
                        if (currentLine.StartsWith("M", StringComparison.OrdinalIgnoreCase))
                        {
                            // Convert the data into string array.
                            arrayData = currentLine.Split(',');
                            sampleNumber = arrayData[0].Substring(1, arrayData[0].Length - 5);

                            // Check the sample number format.
                            // Part 1, M.
                            // Part 2, {Sample Number}.
                            // Part 3, 4 Numbers.
                            // Example, MYBH201307180010048.

                            DateTime testDate = Convert.ToDateTime(string.Format("{0} {1}", arrayData[1], arrayData[2]));

                            if (testDate >= startDate && testDate <= endDate)
                            {
                                deviceAllSampleNumberList.Add(new UnassignedSampleNumberViewModel(null)
                                {
                                    SampleNumber = sampleNumber,
                                    GenerateDateDisplay = testDate.ToString(GeneralUtility.MediumDateTimeFormat)
                                });
                            }
                        }
                    }
                }
            }

            return deviceAllSampleNumberList;
        }
    }
}
